Skip to contentMethod: getType(ELContext, Object, Object)
1: /*
2: * Copyright (c) 1997, 2022 Oracle and/or its affiliates and others.
3: * All rights reserved.
4: * Copyright 2004 The Apache Software Foundation
5: *
6: * Licensed under the Apache License, Version 2.0 (the "License");
7: * you may not use this file except in compliance with the License.
8: * You may obtain a copy of the License at
9: *
10: * http://www.apache.org/licenses/LICENSE-2.0
11: *
12: * Unless required by applicable law or agreed to in writing, software
13: * distributed under the License is distributed on an "AS IS" BASIS,
14: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15: * See the License for the specific language governing permissions and
16: * limitations under the License.
17: */
18:
19: package jakarta.el;
20:
21: /**
22: * Maintains an ordered composite list of child <code>ELResolver</code>s.
23: *
24: * <p>
25: * Though only a single <code>ELResolver</code> is associated with an <code>ELContext</code>, there are usually multiple
26: * resolvers considered for any given variable or property resolution. <code>ELResolver</code>s are combined together
27: * using a <code>CompositeELResolver</code>, to define rich semantics for evaluating an expression.
28: * </p>
29: *
30: * <p>
31: * For the {@link #getValue}, {@link #getType}, {@link #setValue} and {@link #isReadOnly} methods, an
32: * <code>ELResolver</code> is not responsible for resolving all possible (base, property) pairs. In fact, most resolvers
33: * will only handle a <code>base</code> of a single type. To indicate that a resolver has successfully resolved a
34: * particular (base, property) pair, it must set the <code>propertyResolved</code> property of the
35: * <code>ELContext</code> to <code>true</code>. If it could not handle the given pair, it must leave this property
36: * alone. The caller must ignore the return value of the method if <code>propertyResolved</code> is <code>false</code>.
37: * </p>
38: *
39: * <p>
40: * The <code>CompositeELResolver</code> initializes the <code>ELContext.propertyResolved</code> flag to
41: * <code>false</code>, and uses it as a stop condition for iterating through its component resolvers.
42: * </p>
43: *
44: * <p>
45: * The <code>ELContext.propertyResolved</code> flag is not used for the design-time methods
46: * {@link #getFeatureDescriptors} and {@link #getCommonPropertyType}. Instead, results are collected and combined from
47: * all child <code>ELResolver</code>s for these methods.
48: * </p>
49: *
50: * @see ELContext
51: * @see ELResolver
52: * @since Jakarta Server Pages 2.1
53: */
54: public class CompositeELResolver extends ELResolver {
55:
56: public CompositeELResolver() {
57: this.size = 0;
58: this.elResolvers = new ELResolver[16];
59: }
60:
61: /**
62: * Adds the given resolver to the list of component resolvers.
63: *
64: * <p>
65: * Resolvers are consulted in the order in which they are added.
66: * </p>
67: *
68: * @param elResolver The component resolver to add.
69: * @throws NullPointerException If the provided resolver is <code>null</code>.
70: */
71: public void add(ELResolver elResolver) {
72: if (elResolver == null) {
73: throw new NullPointerException();
74: }
75:
76: if (size >= elResolvers.length) {
77: ELResolver[] newResolvers = new ELResolver[size * 2];
78: System.arraycopy(elResolvers, 0, newResolvers, 0, size);
79: elResolvers = newResolvers;
80: }
81:
82: elResolvers[size++] = elResolver;
83: }
84:
85: /**
86: * Attempts to resolve the given <code>property</code> object on the given <code>base</code> object by querying all
87: * component resolvers.
88: *
89: * <p>
90: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
91: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
92: * not <code>true</code> after this method is called, the caller should ignore the return value.
93: *
94: * <p>
95: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
96: *
97: * <p>
98: * Next, for each component resolver in this composite:
99: * <ol>
100: * <li>The <code>getValue()</code> method is called, passing in the provided <code>context</code>, <code>base</code> and
101: * <code>property</code>.</li>
102: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
103: * continues.</li>
104: * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
105: * <code>getValue()</code> is returned by this method.</li>
106: * </ol>
107: *
108: * <p>
109: * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and
110: * the <code>propertyResolved</code> flag remains set to <code>false</code>.
111: *
112: * <p>
113: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
114: *
115: * @param context The context of this evaluation.
116: * @param base The base object whose property value is to be returned, or <code>null</code> to resolve a top-level
117: * variable.
118: * @param property The property or variable to be resolved.
119: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
120: * the result of the variable or property resolution; otherwise undefined.
121: * @throws NullPointerException if context is <code>null</code>
122: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
123: * the specified variable or property does not exist or is not readable.
124: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
125: * exception must be included as the cause property of this exception, if available.
126: */
127: @Override
128: public Object getValue(ELContext context, Object base, Object property) {
129: context.setPropertyResolved(false);
130:
131: Object value = null;
132: for (int i = 0; i < size; i++) {
133: value = elResolvers[i].getValue(context, base, property);
134: if (context.isPropertyResolved()) {
135: return value;
136: }
137: }
138:
139: return null;
140: }
141:
142: /**
143: * Attempts to resolve and invoke the given <code>method</code> on the given <code>base</code> object by querying all
144: * component resolvers.
145: *
146: * <p>
147: * If this resolver handles the given (base, method) pair, the <code>propertyResolved</code> property of the
148: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
149: * not <code>true</code> after this method is called, the caller should ignore the return value.
150: * </p>
151: *
152: * <p>
153: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
154: * </p>
155: *
156: * <p>
157: * Next, for each component resolver in this composite:
158: * <ol>
159: * <li>The <code>invoke()</code> method is called, passing in the provided <code>context</code>, <code>base</code>,
160: * <code>method</code>, <code>paramTypes</code>, and <code>params</code>.</li>
161: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
162: * continues.</li>
163: * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
164: * <code>getValue()</code> is returned by this method.</li>
165: * </ol>
166: *
167: * <p>
168: * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and
169: * the <code>propertyResolved</code> flag remains set to <code>false</code>
170: * </p>
171: * .
172: *
173: * <p>
174: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
175: * </p>
176: *
177: * @param context The context of this evaluation.
178: * @param base The bean on which to invoke the method
179: * @param method The simple name of the method to invoke. Will be coerced to a <code>String</code>.
180: * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use
181: * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter
182: * types are assumed to be unknown.
183: * @param params The parameters to pass to the method, or <code>null</code> if no parameters.
184: *
185: * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type).
186: *
187: * @since Jakarta Expression Language 2.2
188: */
189: @Override
190: public Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params) {
191: context.setPropertyResolved(false);
192:
193: Object value;
194: for (int i = 0; i < size; i++) {
195: value = elResolvers[i].invoke(context, base, method, paramTypes, params);
196: if (context.isPropertyResolved()) {
197: return value;
198: }
199: }
200:
201: return null;
202: }
203:
204: /**
205: * For a given <code>base</code> and <code>property</code>, attempts to identify the most general type that is
206: * acceptable for an object to be passed as the <code>value</code> parameter in a future call to the {@link #setValue}
207: * method. The result is obtained by querying all component resolvers.
208: *
209: * <p>
210: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
211: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
212: * not <code>true</code> after this method is called, the caller should ignore the return value.
213: * </p>
214: *
215: * <p>
216: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
217: * </p>
218: *
219: * <p>
220: * Next, for each component resolver in this composite:
221: * <ol>
222: * <li>The <code>getType()</code> method is called, passing in the provided <code>context</code>, <code>base</code> and
223: * <code>property</code>.</li>
224: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
225: * continues.</li>
226: * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
227: * <code>getType()</code> is returned by this method.</li>
228: * </ol>
229: *
230: * <p>
231: * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and
232: * the <code>propertyResolved</code> flag remains set to <code>false</code>.
233: * </p>
234: *
235: *
236: * <p>
237: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
238: * </p>
239: *
240: * @param context The context of this evaluation.
241: * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level
242: * variable.
243: * @param property The property or variable to return the acceptable type for.
244: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
245: * the most general acceptable type which must be {@code null} if the either the property or the resolver is
246: * read-only; otherwise undefined
247: * @throws NullPointerException if context is <code>null</code>
248: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
249: * the specified variable or property does not exist or is not readable.
250: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
251: * exception must be included as the cause property of this exception, if available.
252: */
253: @Override
254: public Class<?> getType(ELContext context, Object base, Object property) {
255: context.setPropertyResolved(false);
256:
257: Class<?> type;
258:• for (int i = 0; i < size; i++) {
259: type = elResolvers[i].getType(context, base, property);
260:• if (context.isPropertyResolved()) {
261: return type;
262: }
263: }
264:
265: return null;
266: }
267:
268: /**
269: * Attempts to set the value of the given <code>property</code> object on the given <code>base</code> object. All
270: * component resolvers are asked to attempt to set the value.
271: *
272: * <p>
273: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
274: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
275: * not <code>true</code> after this method is called, the caller can safely assume no value has been set.
276: *
277: * <p>
278: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
279: *
280: * <p>
281: * Next, for each component resolver in this composite:
282: * <ol>
283: * <li>The <code>setValue()</code> method is called, passing in the provided <code>context</code>, <code>base</code>,
284: * <code>property</code> and <code>value</code>.</li>
285: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
286: * continues.</li>
287: * <li>Otherwise, iteration stops and no more component resolvers are considered.</li>
288: * </ol>
289: *
290: * <p>
291: * If none of the component resolvers were able to perform this operation, the <code>propertyResolved</code> flag
292: * remains set to <code>false</code>.
293: *
294: * <p>
295: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
296: *
297: * @param context The context of this evaluation.
298: * @param base The base object whose property value is to be set, or <code>null</code> to set a top-level variable.
299: * @param property The property or variable to be set.
300: * @param val The value to set the property or variable to.
301: * @throws NullPointerException if context is <code>null</code>
302: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
303: * the specified variable or property does not exist.
304: * @throws PropertyNotWritableException if the given (base, property) pair is handled by this <code>ELResolver</code>
305: * but the specified variable or property is not writable.
306: * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception
307: * must be included as the cause property of this exception, if available.
308: */
309: @Override
310: public void setValue(ELContext context, Object base, Object property, Object val) {
311: context.setPropertyResolved(false);
312:
313: for (int i = 0; i < size; i++) {
314: elResolvers[i].setValue(context, base, property, val);
315: if (context.isPropertyResolved()) {
316: return;
317: }
318: }
319: }
320:
321: /**
322: * For a given <code>base</code> and <code>property</code>, attempts to determine whether a call to {@link #setValue}
323: * will always fail. The result is obtained by querying all component resolvers.
324: *
325: * <p>
326: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
327: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
328: * not <code>true</code> after this method is called, the caller should ignore the return value.
329: * </p>
330: *
331: * <p>
332: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
333: * </p>
334: *
335: * <p>
336: * Next, for each component resolver in this composite:
337: * <ol>
338: * <li>The <code>isReadOnly()</code> method is called, passing in the provided <code>context</code>, <code>base</code>
339: * and <code>property</code>.</li>
340: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
341: * continues.</li>
342: * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
343: * <code>isReadOnly()</code> is returned by this method.</li>
344: * </ol>
345: *
346: * <p>
347: * If none of the component resolvers were able to perform this operation, the value <code>false</code> is returned and
348: * the <code>propertyResolved</code> flag remains set to <code>false</code>
349: * </p>
350: * .
351: *
352: * <p>
353: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
354: * </p>
355: *
356: * @param context The context of this evaluation.
357: * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level
358: * variable.
359: * @param property The property or variable to return the read-only status for.
360: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
361: * <code>true</code> if the property is read-only or <code>false</code> if not; otherwise undefined.
362: * @throws NullPointerException if context is <code>null</code>
363: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
364: * the specified variable or property does not exist.
365: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
366: * exception must be included as the cause property of this exception, if available.
367: */
368: @Override
369: public boolean isReadOnly(ELContext context, Object base, Object property) {
370: context.setPropertyResolved(false);
371:
372: boolean readOnly;
373: for (int i = 0; i < size; i++) {
374: readOnly = elResolvers[i].isReadOnly(context, base, property);
375: if (context.isPropertyResolved()) {
376: return readOnly;
377: }
378: }
379:
380: return false; // Does not matter
381: }
382:
383: /**
384: * Returns the most general type that this resolver accepts for the <code>property</code> argument, given a
385: * <code>base</code> object. One use for this method is to assist tools in auto-completion. The result is obtained by
386: * querying all component resolvers.
387: *
388: * <p>
389: * The <code>Class</code> returned is the most specific class that is a common superclass of all the classes returned by
390: * each component resolver's <code>getCommonPropertyType</code> method. If <code>null</code> is returned by a resolver,
391: * it is skipped.
392: *
393: * @param context The context of this evaluation.
394: * @param base The base object to return the most general property type for, or <code>null</code> to enumerate the set
395: * of top-level variables that this resolver can evaluate.
396: *
397: * @return <code>null</code> if this <code>ELResolver</code> does not know how to handle the given <code>base</code>
398: * object; otherwise <code>Object.class</code> if any type of <code>property</code> is accepted; otherwise the most
399: * general <code>property</code> type accepted for the given <code>base</code>.
400: */
401: @Override
402: public Class<?> getCommonPropertyType(ELContext context, Object base) {
403: Class<?> commonPropertyType = null;
404: for (int i = 0; i < size; i++) {
405:
406: Class<?> type = elResolvers[i].getCommonPropertyType(context, base);
407: if (type == null) {
408: // skip this Jakarta Expression Language Resolver
409: continue;
410: } else if (commonPropertyType == null) {
411: commonPropertyType = type;
412: } else if (commonPropertyType.isAssignableFrom(type)) {
413: continue;
414: } else if (type.isAssignableFrom(commonPropertyType)) {
415: commonPropertyType = type;
416: } else {
417: // Don't have a commonPropertyType
418: return null;
419: }
420: }
421:
422: return commonPropertyType;
423: }
424:
425: /**
426: * Converts an object to a specific type.
427: *
428: * <p>
429: * An <code>ELException</code> is thrown if an error occurs during the conversion.
430: * </p>
431: *
432: * @param context The context of this evaluation.
433: * @param obj The object to convert.
434: * @param targetType The target type for the convertion.
435: *
436: * @throws ELException thrown if errors occur.
437: *
438: * @since Jakarta Expression Language 3.0
439: */
440: @Override
441: public <T> T convertToType(ELContext context, Object obj, Class<T> targetType) {
442: context.setPropertyResolved(false);
443:
444: T value = null;
445: for (int i = 0; i < size; i++) {
446: value = elResolvers[i].convertToType(context, obj, targetType);
447: if (context.isPropertyResolved()) {
448: return value;
449: }
450: }
451:
452: return null;
453: }
454:
455: private ELResolver[] elResolvers;
456: private int size;
457: }